package com.seas.specs;


import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Seas Cheng on 2017/9/26
 */
public class CustomeSpecs {

    public static <T> Specification<T> byAuto(final EntityManager entityManager, final T example){
        final Class<T> type = (Class<T>) example.getClass(); //获取当前实体类对象类的类型
        return new Specification<T>() {
            @Nullable
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicates = new ArrayList<>(); //新建Predicate列表存储构造的查询条件
                EntityType<T> entity = entityManager.getMetamodel().entity(type); //获取实体类的EntityType, 可以从EntityType获得实体类的属性
                for (Attribute<T, ?> attr: entity.getDeclaredAttributes()){  //对实体类的所有属性做循环
                    Object attrValue =  getValue(example, attr); //获得实体类对象某一个属性的值
                    if (attrValue != null){
                        if (attr.getJavaType() == String.class){ //当属性值为字符串类型时
                            if (!StringUtils.isEmpty(attrValue)){ //当前字符不为空的情况
                                //构造like
                                predicates.add(cb.like(root.get(attribute(entity, attr.getName(), String.class)), pattern((String) attrValue)));
                            }
                        }else {
                            //其余情况，构造equal查询条件
                            predicates.add(cb.equal(root.get(attribute(entity, attr.getName(), attrValue.getClass())), attrValue));
                        }
                    }
                }

                return predicates.isEmpty()?cb.conjunction():cb.and(predicates.toArray(new Predicate[] {}));
            }

            //通过反射获得实体类对象对应属性的属性值
            private <T> Object getValue(T example, Attribute<T, ?> attr){
                return ReflectionUtils.getField((Field)attr.getJavaMember(), example);
            }

            //获得实体类的当前属性的SingularAttribute， SingularAttribute包含的是实体类的某个单独属性
            private <E, T>SingularAttribute<T, E> attribute(EntityType<T> entity, String fieldName, Class<E> fieldClass){
                return entity.getDeclaredSingularAttribute(fieldName, fieldClass);
            }

        };
    }
    // 构造like 的查询模式， 即前后加%
    static private String pattern(String str){
        return "%" +str + "%";
    }
}
